package com.google.zxing.journeyapps.barcodescanner;

import com.google.zxing.journeyapps.barcodescanner.camera.CameraInstance;
import com.google.zxing.journeyapps.barcodescanner.camera.CameraSettings;
import com.google.zxing.journeyapps.barcodescanner.camera.CameraSurface;
import com.google.zxing.journeyapps.barcodescanner.camera.CameraParametersCallback;
import com.google.zxing.journeyapps.barcodescanner.camera.DisplayConfiguration;
import com.google.zxing.journeyapps.barcodescanner.camera.PreviewScalingStrategy;
import com.google.zxing.journeyapps.barcodescanner.camera.CenterCropStrategy;
import com.google.zxing.journeyapps.barcodescanner.camera.FitCenterStrategy;
import com.google.zxing.journeyapps.barcodescanner.camera.FitXYStrategy;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.Attr;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.WindowManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.eventhandler.InnerEvent;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;

/**
 * CameraPreview is a view that handles displaying of a camera preview on a SurfaceView. It is
 * intended to be used as a base for realtime processing of camera images, e.g. barcode decoding
 * or OCR, although none of this happens in CameraPreview itself.
 *
 * The camera is managed on a separate thread, using CameraInstance.
 *
 * Two methods MUST be called on CameraPreview to manage its state:
 * 1. resume() - initialize the camera and start the preview. Call from the Ability's onResume().
 * 2. pause() - stop the preview and release any resources. Call from the Ability's onPause().
 *
 * Startup sequence:
 *
 * 1. Create SurfaceView.
 * 2. open camera.
 * 2. layout this container, to get size
 * 3. set display config, according to the container size
 * 4. configure()
 * 5. wait for preview size to be ready
 * 6. set surface size according to preview size
 * 7. set surface and start preview
 */
public class CameraPreview extends ComponentContainer implements Component.BindStateChangedListener, Component.LayoutRefreshedListener, BusHelper.Observable {
    public static final int zxing_prewiew_size_ready = 1;
    public static final int zxing_camera_error = 2;
    public static final int zxing_camera_closed = 3;


    public interface StateListener {
        /**
         * Preview and frame sizes are determined.
         */
        void previewSized();

        /**
         * Preview has started.
         */
        void previewStarted();

        /**
         * Preview has stopped.
         */
        void previewStopped();

        /**
         * The camera has errored, and cannot display a preview.
         *
         * @param error the error
         */
        void cameraError(Exception error);

        /**
         * The camera has been closed.
         */
        void cameraClosed();
    }

    private static final String TAG = CameraPreview.class.getSimpleName();

    private CameraInstance cameraInstance;

    private boolean useTextureView = false;

    private SurfaceProvider surfaceView;

    private SurfaceProvider textureView;

    private boolean previewActive = false;

    private RotationListener rotationListener;
    private int openedOrientation = -1;

    // Delay after rotation change is detected before we reorientate ourselves.
    // This is to avoid double-reinitialization when the Ability is destroyed and recreated.
    private static final int ROTATION_LISTENER_DELAY_MS = 250;

    private List<StateListener> stateListeners = new ArrayList<>();

    private DisplayConfiguration displayConfiguration;
    private CameraSettings cameraSettings = new CameraSettings();

    // Size of this container, non-null after layout is performed
    private Size containerSize;

    // Size of the preview resolution
    private Size previewSize;

    // Rect placing the preview surface
    private Rect surfaceRect;

    // Size of the current surface. non-null if the surface is ready
    private Size currentSurfaceSize;

    // Framing rectangle relative to this view
    private Rect framingRect = null;

    // Framing rectangle relative to the preview resolution
    private Rect previewFramingRect = null;

    // Size of the framing rectangle. If null, defaults to using a margin percentage.
    private Size framingRectSize = null;

    // Fraction of the width / heigth to use as a margin. This fraction is used on each size, so
    // must be smaller than 0.5;
    private double marginFraction = 0.1d;

    private PreviewScalingStrategy previewScalingStrategy = null;

    private boolean torchOn = false;

    private EventHandler stateHandler = new EventHandler(EventRunner.getMainEventRunner()) {

        @Override
        protected void processEvent(InnerEvent event) {
            if (event.eventId == zxing_prewiew_size_ready) {
                previewSized((Size) event.object);
            } else if (event.eventId == zxing_camera_error) {
                Exception error = (Exception) event.object;

                if (isActive()) {
                    // This check prevents multiple errors from begin passed through.
                    pause();
                    fireState.cameraError(error);
                }
            } else if (event.eventId == zxing_camera_closed) {
                fireState.cameraClosed();
            }
        }
    };

    private final SurfaceOps.Callback surfaceCallback = new SurfaceOps.Callback() {

        @Override
        public void surfaceCreated(SurfaceOps surfaceOps) {

        }

        @Override
        public void surfaceChanged(SurfaceOps surfaceOps, int format, int width, int height) {
            if (surfaceOps == null || surfaceOps.getSurface() == null) {
                return;
            }
            currentSurfaceSize = new Size(width, height);
            startPreviewIfReady();
        }

        @Override
        public void surfaceDestroyed(SurfaceOps surfaceOps) {
            currentSurfaceSize = null;
        }
    };

    private RotationCallback rotationCallback = new RotationCallback() {
        @Override
        public void onRotationChanged(int rotation) {
            // Make sure this is run on the main thread.
            stateHandler.postTask(() -> rotationChanged(), ROTATION_LISTENER_DELAY_MS);
        }
    };

    public CameraPreview(Context context) {
        super(context);
        initialize(context, null, null, 0);
    }

    public CameraPreview(Context context, AttrSet attrs) {
        super(context, attrs);
        initialize(context, attrs, null, 0);
    }

    public CameraPreview(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize(context, attrs, defStyleAttr, 0);
    }

    private void initialize(Context context, AttrSet attrs, String defStyleAttr, int defStyleRes) {
        Logger.getLogger(TAG).warning("CameraPreview initialize start");
        setLayoutRefreshedListener(this);
        setBindStateChangedListener(this);
        if (getBackgroundElement() == null) {
            // Default to SurfaceView colour, so that there are less changes.
            // TODO 设置背景色为黑色 ResourceTable.Color_black，暂时未知 Element对象的构建方法
            ShapeElement stateElement = new ShapeElement();
            stateElement.setRgbColor(RgbColor.fromArgbInt(Color.BLACK.getValue()));
            setBackground(stateElement);
        }
        Logger.getLogger(TAG).warning("CameraPreview initialize start 22");
        initializeAttributes(attrs);

//        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

        /*stateHandler = new EventHandler(EventRunner.getMainEventRunner()){
            @Override
            protected void processEvent(InnerEvent event) {
                super.processEvent(event);
                if (event.eventId == zxing_prewiew_size_ready) {
                    // At this point, we have the camera preview size, and should have containerSize and
                    // surfaceRect.
                    previewSized((Size) event.object);
                } else if (event.eventId == zxing_camera_error) {
                    Exception error = (Exception) event.object;

                    if (isActive()) {
                        // This check prevents multiple errors from begin passed through.
                        pause();
                        fireState.cameraError(error);
                    }
                } else if (event.eventId == zxing_camera_closed) {
                    fireState.cameraClosed();
                }
            }
        };*/

        rotationListener = new RotationListener();
    }


    /**
     * Initialize from XML attributes.
     *
     * @param attrs the attributes
     */
    protected void initializeAttributes(AttrSet attrs) {
        int framingRectWidth = -1;
        int framingRectHeight = -1;
        Optional<Attr> optional = attrs.getAttr("zxing_framing_rect_width");

        Logger.getLogger(TAG).warning("CameraPreview initializeAttributes start");
        if (optional.isPresent()) {
            framingRectWidth = optional.get().getIntegerValue();
        }
        optional = attrs.getAttr("zxing_framing_rect_height");
        if (optional.isPresent()) {
            framingRectHeight = optional.get().getIntegerValue();
        }
        if (framingRectWidth > 0 && framingRectHeight > 0) {
            this.framingRectSize = new Size(framingRectWidth, framingRectHeight);
        }
        optional = attrs.getAttr("zxing_use_texture_view");
        this.useTextureView = true;
        optional.ifPresent(attr -> this.useTextureView = attr.getBoolValue());

        // See zxing_attrs.xml for the enum values
        int scalingStrategyNumber = -1;
        optional = attrs.getAttr("zxing_preview_scaling_strategy");
        if (optional.isPresent()) {
            scalingStrategyNumber = optional.get().getIntegerValue();
        }
        if (scalingStrategyNumber == 1) {
            previewScalingStrategy = new CenterCropStrategy();
        } else if (scalingStrategyNumber == 2) {
            previewScalingStrategy = new FitCenterStrategy();
        } else if (scalingStrategyNumber == 3) {
            previewScalingStrategy = new FitXYStrategy();
        }
    }

    @Override
    public void onComponentBoundToWindow(Component component) {
        setupSurfaceView();
    }

    @Override
    public void onComponentUnboundFromWindow(Component component) {

    }

    private void rotationChanged() {
        // Confirm that it did actually change
        if (isActive() && getDisplayRotation() != openedOrientation) {
            pause();
            resume();
        }
    }

    private void setupSurfaceView() {
        Logger.getLogger(TAG).severe("setupSurfaceView");
        if (useTextureView) {
            textureView = new SurfaceProvider(getContext());
            Optional<SurfaceOps> optSurfaceOps = textureView.getSurfaceOps();
            if (optSurfaceOps.isPresent()) {
                SurfaceOps surfaceOps = optSurfaceOps.get();
                surfaceOps.addCallback(surfaceCallback);
                addComponent(textureView);
                Logger.getLogger(TAG).severe("setupSurfaceView optSurfaceOps.isPresent");
            } else {
                Logger.getLogger(TAG).severe("创建显示控件textureView失败");
            }
        } else {
            Logger.getLogger(TAG).severe("setupSurfaceView un useTextureView");
            surfaceView = new SurfaceProvider(getContext());
            Optional<SurfaceOps> optSurfaceOps = surfaceView.getSurfaceOps();
            if (optSurfaceOps.isPresent()) {
                SurfaceOps surfaceOps = optSurfaceOps.get();
                surfaceOps.addCallback(surfaceCallback);
                addComponent(surfaceView);
            } else {
                Logger.getLogger(TAG).severe("创建显示控件surfaceView失败");
            }
        }
    }

    /**
     * Add a listener to be notified of changes to the preview state, as well as camera errors.
     *
     * @param listener the listener
     */
    public void addStateListener(StateListener listener) {
        stateListeners.add(listener);
    }

    private final StateListener fireState = new StateListener() {
        @Override
        public void previewSized() {
            Logger.getLogger(TAG).warning("fireState.previewSized");
            for (StateListener listener : stateListeners) {
                listener.previewSized();
            }
        }

        @Override
        public void previewStarted() {
            for (StateListener listener : stateListeners) {
                listener.previewStarted();
            }

        }

        @Override
        public void previewStopped() {
            for (StateListener listener : stateListeners) {
                listener.previewStopped();
            }
        }

        @Override
        public void cameraError(Exception error) {
            for (StateListener listener : stateListeners) {
                listener.cameraError(error);
            }
        }

        @Override
        public void cameraClosed() {
            for (StateListener listener : stateListeners) {
                listener.cameraClosed();
            }
        }
    };

    private void calculateFrames() {
        if (containerSize == null || previewSize == null || displayConfiguration == null) {
            previewFramingRect = null;
            framingRect = null;
            surfaceRect = null;
            throw new IllegalStateException("containerSize or previewSize is not set yet");
        }

        int previewWidth = previewSize.width;
        int previewHeight = previewSize.height;

        int width = containerSize.width;
        int height = containerSize.height;

        Logger.getLogger(TAG).warning("previewSize " + previewSize.toString());
        Rect scaledPreview = displayConfiguration.scalePreview(previewSize);
        if (scaledPreview.getWidth() <= 0 || scaledPreview.getHeight() <= 0) {
            // Something is not ready yet - we can't start the preview.
            return;
        }

        surfaceRect = scaledPreview;
        Logger.getLogger(TAG).warning("surfaceRect " + surfaceRect.toString());

        Rect container = new Rect(0, 0, width, height);
        Logger.getLogger(TAG).warning("container " + container.toString());
        framingRect = calculateFramingRect(container, surfaceRect);
        Rect frameInPreview = new Rect(framingRect);
        Logger.getLogger(TAG).warning("frameInPreview " + frameInPreview.toString());

        frameInPreview.translate(-surfaceRect.left, -surfaceRect.top);
        Logger.getLogger(TAG).warning("frameInPreview " + frameInPreview.toString());

        previewFramingRect = new Rect(frameInPreview.left * previewWidth / surfaceRect.getWidth(),
                frameInPreview.top * previewHeight / surfaceRect.getHeight(),
                frameInPreview.right * previewWidth / surfaceRect.getWidth(),
                frameInPreview.bottom * previewHeight / surfaceRect.getHeight());
        Logger.getLogger(TAG).warning("previewFramingRect " + previewFramingRect.toString());

        if (previewFramingRect == null || previewFramingRect.getWidth() <= 0 || previewFramingRect.getHeight() <= 0) {
            previewFramingRect = null;
            framingRect = null;
        } else {
            fireState.previewSized();
        }
    }

    /**
     * Call this on the main thread, while the preview is running.
     *
     * @param on true to turn on the torch
     */
    public void setTorch(boolean on) {
        torchOn = on;
        if (cameraInstance != null) {
            cameraInstance.setTorch(on);
        }
    }

    /**
     * Changes the settings for Camera.
     * Must be called after {@link #resume()}.
     *
     * @param callback {@link CameraParametersCallback}
     */
    public void changeCameraParameters(CameraParametersCallback callback) {
        if (cameraInstance != null) {
            cameraInstance.changeCameraParameters(callback);
        }
    }

    private void containerSized(Size containerSize) {
        this.containerSize = containerSize;
        if (cameraInstance != null) {
            if (cameraInstance.getDisplayConfiguration() == null && cameraInstance.isOpen()) {
                displayConfiguration = new DisplayConfiguration(getDisplayRotation(), containerSize);
                displayConfiguration.setPreviewScalingStrategy(getPreviewScalingStrategy());
                cameraInstance.setDisplayConfiguration(displayConfiguration);
                cameraInstance.configureCamera();
                if (torchOn) {
                    cameraInstance.setTorch(torchOn);
                }
            }
        }
    }

    /**
     * Override the preview scaling strategy.
     *
     * @param previewScalingStrategy null for the default
     */
    public void setPreviewScalingStrategy(PreviewScalingStrategy previewScalingStrategy) {
        this.previewScalingStrategy = previewScalingStrategy;
    }

    /**
     * Override this to specify a different preview scaling strategy.
     */
    public PreviewScalingStrategy getPreviewScalingStrategy() {
        if (previewScalingStrategy != null) {
            return previewScalingStrategy;
        }

        // If we are using SurfaceTexture, it is safe to use centerCrop.
        // For SurfaceView, it's better to use fitCenter, otherwise the preview may overlap to
        // other views.
        if (textureView != null) {
            return new CenterCropStrategy();
        } else {
            return new FitCenterStrategy();
        }

    }

    private void previewSized(Size size) {
        this.previewSize = size;
        if (containerSize != null) {
            calculateFrames();
//            requestLayout();
            postLayout();
            startPreviewIfReady();
        }
    }

    /**
     * Calculate transformation for the TextureView.
     *
     * An identity matrix would cause the preview to be scaled up/down to fill the TextureView.
     *
     * @param textureSize the size of the textureView
     * @param previewSize the camera preview resolution
     * @return the transform matrix for the TextureView
     */
    protected Matrix calculateTextureTransform(Size textureSize, Size previewSize) {
        float ratioTexture = (float) textureSize.width / (float) textureSize.height;
        float ratioPreview = (float) previewSize.width / (float) previewSize.height;

        float scaleX;
        float scaleY;

        // We scale so that either width or height fits exactly in the TextureView, and the other
        // is bigger (cropped).
        if (ratioTexture < ratioPreview) {
            scaleX = ratioPreview / ratioTexture;
            scaleY = 1;
        } else {
            scaleX = 1;
            scaleY = ratioTexture / ratioPreview;
        }

        Matrix matrix = new Matrix();

        matrix.setScale(scaleX, scaleY);

        // Center the preview
        float scaledWidth = textureSize.width * scaleX;
        float scaledHeight = textureSize.height * scaleY;
        float dx = (textureSize.width - scaledWidth) / 2;
        float dy = (textureSize.height - scaledHeight) / 2;

        // Perform the translation on the scaled preview
        matrix.postTranslate(dx, dy);

        return matrix;
    }

    private void startPreviewIfReady() {
        if (currentSurfaceSize == null) {
            setupSurfaceView();
            return;
        }
        if (currentSurfaceSize != null && previewSize != null && surfaceRect != null) {
            if (surfaceView != null && currentSurfaceSize.equals(new Size(surfaceRect.getWidth(), surfaceRect.getHeight()))) {
                startCameraPreview(new CameraSurface(surfaceView.getSurfaceOps().get().getSurface()));
            } else if (textureView != null && textureView.getSurfaceOps().isPresent() && textureView.getSurfaceOps().get().getSurface() != null) {

                startCameraPreview(new CameraSurface(textureView.getSurfaceOps().get().getSurface()));
            } else {
                // Surface is not the correct size yet
            }
        }
    }


    //    @SuppressLint("DrawAllocation")
    /*@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        containerSized(new Size(r - l, b - t));

        if (surfaceView != null) {
            if (surfaceRect == null) {
                // Match the container, to reduce the risk of issues. The preview should never be drawn
                // while the surface has this size.
                // todo 设置布局摆放
//                surfaceView.layout(0, 0, getWidth(), getHeight());
            } else {
                // todo 设置布局摆放
//                surfaceView.layout(surfaceRect.left, surfaceRect.top, surfaceRect.right, surfaceRect.bottom);
            }
        } else if (textureView != null) {
            // todo 设置布局摆放
//            textureView.layout(0, 0, getWidth(), getHeight());
        }
    }*/


    /**
     * The framing rectangle, relative to this view. Use to draw the rectangle.
     *
     * Will never be null while the preview is active.
     *
     * @return the framing rect, or null
     * @see #isPreviewActive()
     */
    public Rect getFramingRect() {
        return framingRect;
    }

    /**
     * The framing rect, relative to the camera preview resolution.
     *
     * Will never be null while the preview is active.
     *
     * @return the preview rect, or null
     * @see #isPreviewActive()
     */
    public Rect getPreviewFramingRect() {
        return previewFramingRect;
    }

    public Size getPreviewSize() {
        return previewSize;
    }

    /**
     * @return the CameraSettings currently in use
     */
    public CameraSettings getCameraSettings() {
        return cameraSettings;
    }

    /**
     * Set the CameraSettings. Use this to select a different camera, change exposure and torch
     * settings, and some other options.
     *
     * This has no effect if the camera is already open.
     *
     * @param cameraSettings the new settings
     */
    public void setCameraSettings(CameraSettings cameraSettings) {
        this.cameraSettings = cameraSettings;
    }


    @Override
    public void onSubscribe(Object object) {
        BusHelper.getINSTANCE().unRegister(this);
        postLayout();
    }

    @Override
    public void onRefreshed(Component component) {
        int left = getLeft();
        int top = getTop();
        int right = getRight();
        int bottom = getBottom();
        containerSized(new Size(right - left, bottom - top));

        if (surfaceView != null) {
            if (surfaceRect == null) {
                // Match the container, to reduce the risk of issues. The preview should never be drawn
                // while the surface has this size.
                // todo 设置布局摆放
                surfaceView.setComponentPosition(0,0,getWidth(), getHeight());
                surfaceView.postLayout();
            } else {
                // todo 设置布局摆放
                surfaceView.setComponentPosition(surfaceRect);
                surfaceView.postLayout();
            }
        } else if (textureView != null) {
            // todo 设置布局摆放
            textureView.setComponentPosition(0,0,getWidth(), getHeight());
            textureView.postLayout();
        }
    }

    /**
     * Start the camera preview and decoding. Typically this should be called from the Activity's
     * onResume() method.
     *
     * Call from UI thread only.
     */
    public void resume() {
        // This must be safe to call multiple times
        Util.validateMainThread();
        Logger.getLogger(TAG).config("resume()");

        // initCamera() does nothing if called twice, but does log a warning
        initCamera();

        if (currentSurfaceSize != null) {
            // The activity was paused but not stopped, so the surface still exists. Therefore
            // surfaceCreated() won't be called, so init the camera here.
            startPreviewIfReady();
        } else if (surfaceView != null) {
            // Install the callback and wait for surfaceCreated() to init the camera.
            surfaceView.getSurfaceOps().get().addCallback(surfaceCallback);
        } else if (textureView != null) {
            textureView.getSurfaceOps().get().addCallback(surfaceCallback);
            /*if (textureView.isAvailable()) {
                surfaceTextureListener().onSurfaceTextureAvailable(textureView.getSurfaceTexture(), textureView.getWidth(), textureView.getHeight());
            } else {
                textureView.setSurfaceTextureListener(surfaceTextureListener());
            }*/
        }

        // To trigger surfaceSized again
        postLayout();
        rotationListener.listen(getContext(), rotationCallback);
    }

    /**
     * Pause scanning and the camera preview. Typically this should be called from the Activity's
     * onPause() method.
     *
     * Call from UI thread only.
     */
    public void pause() {
        // This must be safe to call multiple times.
        Util.validateMainThread();
        Logger.getLogger(TAG).config("pause()");

        openedOrientation = -1;
        if (cameraInstance != null) {
            cameraInstance.close();
            cameraInstance = null;
            previewActive = false;
        } else {
            stateHandler.sendEvent(zxing_camera_closed);
        }
        if (currentSurfaceSize == null && surfaceView != null) {
            SurfaceOps surfaceHolder = surfaceView.getSurfaceOps().get();
            surfaceHolder.removeCallback(surfaceCallback);
        }
        if (currentSurfaceSize == null && textureView != null) {
//            textureView.setSurfaceTextureListener(null);
            SurfaceOps surfaceHolder = textureView.getSurfaceOps().get();
            surfaceHolder.removeCallback(surfaceCallback);
        }

        this.containerSize = null;
        this.previewSize = null;
        this.previewFramingRect = null;
        rotationListener.stop();

        fireState.previewStopped();
    }

    /**
     * Pause scanning and preview; waiting for the Camera to be closed.
     *
     * This blocks the main thread.
     */
    public void pauseAndWait() {
        CameraInstance instance = getCameraInstance();
        pause();
        long startTime = System.nanoTime();
        while (instance != null && !instance.isCameraClosed()) {
            if (System.nanoTime() - startTime > 2000000000) {
                // Don't wait for longer than 2 seconds
                break;
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                break;
            }
        }
    }

    public Size getFramingRectSize() {
        return framingRectSize;
    }

    /**
     * Set an exact size for the framing rectangle. It will be centered in the view.
     *
     * @param framingRectSize the size
     */
    public void setFramingRectSize(Size framingRectSize) {
        this.framingRectSize = framingRectSize;
    }

    public double getMarginFraction() {
        return marginFraction;
    }

    /**
     * The the fraction of the width/height of view to be used as a margin for the framing rect.
     * This is ignored if framingRectSize is specified.
     *
     * @param marginFraction the fraction
     */
    public void setMarginFraction(double marginFraction) {
        if (marginFraction >= 0.5d) {
            throw new IllegalArgumentException("The margin fraction must be less than 0.5");
        }
        this.marginFraction = marginFraction;
    }

    public boolean isUseTextureView() {
        return useTextureView;
    }

    /**
     * Set to true to use TextureView instead of SurfaceView.
     *
     * Will only have an effect on API >= 14.
     *
     * @param useTextureView true to use TextureView.
     */
    public void setUseTextureView(boolean useTextureView) {
        this.useTextureView = useTextureView;
    }

    /**
     * Considered active if between resume() and pause().
     *
     * @return true if active
     */
    protected boolean isActive() {
        return cameraInstance != null;
    }

    private int getDisplayRotation() {
        // todo 获取显示器（屏幕）旋转角度
//        return windowManager.getDefaultDisplay().getRotation();
        return 0;
    }

    private void initCamera() {
        if (cameraInstance != null) {
            return;
        }

        cameraInstance = createCameraInstance();

        cameraInstance.setReadyHandler(stateHandler);
        cameraInstance.open();

        // Keep track of the orientation we opened at, so that we don't reopen the camera if we
        // don't need to.
        openedOrientation = getDisplayRotation();
        BusHelper.getINSTANCE().register("cameraInitCompleted2",this);
    }

    /**
     * Create a new CameraInstance.
     *
     * Override to use a custom CameraInstance.
     *
     * @return a new CameraInstance
     */
    protected CameraInstance createCameraInstance() {
        CameraInstance cameraInstance = new CameraInstance(getContext());
        cameraInstance.setCameraSettings(cameraSettings);
        return cameraInstance;
    }

    private void startCameraPreview(CameraSurface surface) {
        if (!previewActive && cameraInstance != null) {
            cameraInstance.setSurface(surface);
            cameraInstance.startPreview();
            previewActive = true;

            previewStarted();
            fireState.previewStarted();
        }
    }

    /**
     * Called when the preview is started. Override this to start decoding work.
     */
    protected void previewStarted() {

    }

    /**
     * Get the current CameraInstance. This may be null, and may change when
     * pausing / resuming the preview.
     *
     * While the preview is active, getCameraInstance() will never be null.
     *
     * @return the current CameraInstance
     * @see #isPreviewActive()
     */
    public CameraInstance getCameraInstance() {
        return cameraInstance;
    }

    /**
     * The preview typically starts being active a while after calling resume(), and stops
     * when calling pause().
     *
     * @return true if the preview is active
     */
    public boolean isPreviewActive() {
        return previewActive;
    }

    /**
     * Calculate framing rectangle, relative to the preview frame.
     *
     * Note that the SurfaceView may be larger than the container.
     *
     * Override this for more control over the framing rect calculations.
     *
     * @param container this container, with left = top = 0
     * @param surface   the SurfaceView, relative to this container
     * @return the framing rect, relative to this container
     */
    protected Rect calculateFramingRect(Rect container, Rect surface) {
        // intersection is the part of the container that is used for the preview
        Rect intersection = new Rect(container);
//        boolean intersects = intersection.intersect(surface);
        boolean intersects = intersection.getIntersectRect(surface);

        if (framingRectSize != null) {
            // Specific size is specified. Make sure it's not larger than the container or surface.
            int horizontalMargin = Math.max(0, (intersection.getWidth() - framingRectSize.width) / 2);
            int verticalMargin = Math.max(0, (intersection.getHeight() - framingRectSize.height) / 2);

            intersection.shrink(horizontalMargin, verticalMargin);

            return intersection;
        }
        // margin as 10% (default) of the smaller of width, height
        int margin = (int)Math.min(intersection.getWidth() * marginFraction, intersection.getHeight() * marginFraction);
        intersection.shrink(margin, margin);
        if (intersection.getHeight() > intersection.getWidth()) {
            // We don't want a frame that is taller than wide.
            intersection.shrink(0, (intersection.getHeight() - intersection.getWidth()) / 2);
        }
        return intersection;
    }

    // todo 保存和恢复状态

    /*@Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();

//        Bundle myState = new Bundle();
//        myState.putParcelable("super", superState);
//        myState.putBoolean("torch", torchOn);
        PacMap myState=new PacMap();
        myState.putSequenceableObject("super", superState);
        myState.putBooleanValue("torch", torchOn);
        return myState;
    }

    @Override
    protected void onRestoreInstanceState(PacMap state) {
        if (!(state instanceof PacMap*//*Bundle*//*)) {
            super.onRestoreInstanceState(state);
            return;
        }
        //Bundle myState = (Bundle)state;
        PacMap myState=(PacMap) state;
        Parcelable superState = myState.getParcelable("super");
        super.onRestoreInstanceState(superState);
        boolean torch = myState.getBoolean("torch");
        setTorch(torch);
    }*/

    /**
     *
     * @return true if the camera has been closed in a background thread.
     */
    public boolean isCameraClosed() {
        return cameraInstance == null || cameraInstance.isCameraClosed();
    }
}
